Ising Model

Rust를 이용하여 상호작용 가능한 Ising 모델을 작성할 수 있습니다. nannou를 사용하여 모델을 그리고, nannou_egui를 사용하여 상호작용할 수 있는 GUI를 만듭니다.
use nannou::prelude::*;
use nannou_egui::{egui, Egui};
use rand::prelude::*;

fn main() {
    nannou::app(model)
        .update(update)
        .run();
}
struct Model {
    n: usize,
    temp: f64,
    iter: i32,
    board: Vec,
    egui: Egui
}
fn model(app: &App) -> Model {
    let window_id = app.new_window()
        .size(900, 600)
        .raw_event(raw_window_event)
        .view(view)
        .build()
        .unwrap();
    let window = app.window(window_id).unwrap();
    let n = 100;
    let mut board = vec![false; n * n];
    for i in 0..n {
        for j in 0..n {
            if rand::random() {
                board[i + j * n] = true;
            }
        }
    }
    Model {
        n,
        temp: 2.0,
        iter: 10000,
        board,
        egui: Egui::from_window(&window)
    }
}
fn update(_app: &App, model: &mut Model, _update: Update) {
    let egui = &mut model.egui;
    let ctx = egui.begin_frame();

    egui::Window::new("Settings").show(&ctx, |ui| {
        ui.label("Temperature:");
        ui.add(egui::Slider::new(&mut model.temp, 0.0..=10.0));
        ui.label("Iter/Update:");
        ui.add(egui::Slider::new(&mut model.iter, 1..=100000));
    });
    
    let mut rng = rand::thread_rng();
    let n = model.n as i32;
    let dirs: [(i32, i32); 4] = [(0, 1), (0, -1), (1, 0), (-1, 0)];
    for _ in 0..model.iter {
        let i = rng.gen_range(0..n);
        let j = rng.gen_range(0..n);
        let mut spin_sum = 0;
        for (dx, dy) in &dirs {
            let (nx, ny) = ((i + dx + n) % n, (j + dy + n) % n);
            spin_sum += 2 * (model.board[(nx + ny * n) as usize] as i32) - 1;
        }
        let delta_energy = 2 * spin_sum * (2 * (model.board[(i + j * n) as usize] as i32) - 1);
        if delta_energy <= 0 || (-delta_energy as f64 / model.temp).exp() > rng.gen() {
            model.board[(i + j * n) as usize] = !model.board[(i + j * n) as usize];
        }
    }
}
fn raw_window_event(_app: &App, model: &mut Model, event: &nannou::winit::event::WindowEvent) {
    model.egui.handle_raw_event(event);
}
fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();
    draw.background().color(BLACK);
    let n = model.n;
    let full_width = 500.0;
    let unit_width = full_width / n as f32;
    for i in 0..n {
        for j in 0..n {
            draw.rect()
                .color(match model.board[i + j * n] {
                    true => LIGHTCORAL,
                    _ => LIGHTBLUE
                })
                .w(unit_width)
                .h(unit_width)
                .x(-full_width / 2.0 + j as f32 * unit_width)
                .y(-full_width / 2.0 + i as f32 * unit_width);
        }
    }
    draw.to_frame(app, &frame).unwrap();
    model.egui.draw_to_frame(&frame).unwrap();
}